#include "wx/wxprec.h"
#if wxUSE_SOCKETS

#include "wx/socket.h"

#ifndef WX_PRECOMP
#include "wx/object.h"
#include "wx/string.h"
#include "wx/intl.h"
#include "wx/log.h"
#include "wx/event.h"
#include "wx/app.h"
#include "wx/utils.h"
#include "wx/timer.h"
#include "wx/module.h"
#endif

#include "wx/apptrait.h"
#include "wx/sckaddr.h"
#include "wx/datetime.h"
#include "wx/build.h"

// wxNet

#define MAX_DISCARD_SIZE (10 * 1024)

#if wxUSE_THREADS
#define PROCESS_EVENTS()        \
  {                               \
    if ( wxThread::IsMain() )   \
      wxYield();              \
    else                        \
      wxThread::Yield();      \
  }
#else // !wxUSE_THREADS
#define PROCESS_EVENTS() wxYield()
#endif // wxUSE_THREADS/!wxUSE_THREADS

#define wxTRACE_Socket _T("wxSocket")

IMPLEMENT_CLASS( wxSocketBase, wxObject )
IMPLEMENT_CLASS( wxSocketServer, wxSocketBase )
IMPLEMENT_CLASS( wxSocketClient, wxSocketBase )
IMPLEMENT_CLASS( wxDatagramSocket, wxSocketBase )
IMPLEMENT_DYNAMIC_CLASS( wxSocketEvent, wxEvent )

class wxSocketState : public wxObject {
  public:
    wxSocketFlags m_flags;
    wxSocketEventFlags m_eventmask;
    bool m_notify;
    void *m_clientData;

  public:
    wxSocketState() : wxObject() {}

    DECLARE_NO_COPY_CLASS( wxSocketState )
};

size_t wxSocketBase::m_countInit = 0;

bool wxSocketBase::IsInitialized() {
  return m_countInit > 0;
}

bool wxSocketBase::Initialize() {
  if( !m_countInit++ ) {
    wxASSERT_MSG( wxIsMainThread(),
                  wxT( "Call wxSocketBase::Initialize() from the main thread first!" ) );
    wxAppTraits *traits = wxAppConsole::GetInstance() ?
                          wxAppConsole::GetInstance()->GetTraits() : NULL;
    GSocketGUIFunctionsTable *functions =
      traits ? traits->GetSocketGUIFunctionsTable() : NULL;
    GSocket_SetGUIFunctions( functions );
    if( !GSocket_Init() ) {
      m_countInit--;
      return false;
    }
  }
  return true;
}

void wxSocketBase::Shutdown() {
  wxASSERT_MSG( m_countInit, _T( "extra call to Shutdown()" ) );
  if( --m_countInit == 0 ) {
    GSocket_Cleanup();
  }
}

void wxSocketBase::Init() {
  m_socket = NULL;
  m_type = wxSOCKET_UNINIT;
  m_flags = 0;
  m_connected =
    m_establishing =
      m_reading =
        m_writing =
          m_error = false;
  m_lcount = 0;
  m_timeout = 600;
  m_beingDeleted = false;
  m_unread = NULL;
  m_unrd_size = 0;
  m_unrd_cur = 0;
  m_id = wxID_ANY;
  m_handler = NULL;
  m_clientData = NULL;
  m_notify = false;
  m_eventmask = 0;
  if( !IsInitialized() ) {
    Initialize();
  }
}

wxSocketBase::wxSocketBase() {
  Init();
}

wxSocketBase::wxSocketBase( wxSocketFlags flags, wxSocketType type ) {
  Init();
  m_flags = flags;
  m_type  = type;
}

wxSocketBase::~wxSocketBase() {
  wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
  if( traits ) {
    traits->RemoveFromPendingDelete( this );
  }
  if( !m_beingDeleted ) {
    Close();
  }
  if( m_socket ) {
    delete m_socket;
  }
  if( m_unread ) {
    free( m_unread );
  }
}

bool wxSocketBase::Destroy() {
  m_beingDeleted = true;
  Close();
  Notify( false );
  wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
  if( traits ) {
    traits->ScheduleForDestroy( this );
  } else
  { delete this; }
  return true;
}

bool wxSocketBase::Close() {
  InterruptWait();
  if( m_socket ) {
    m_socket->UnsetCallback( GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG |
                             GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG );
    m_socket->Shutdown();
  }
  m_connected = false;
  m_establishing = false;
  return true;
}

wxSocketBase& wxSocketBase::Read( void* buffer, wxUint32 nbytes ) {
  m_reading = true;
  m_lcount = _Read( buffer, nbytes );
  if( m_flags & wxSOCKET_WAITALL ) {
    m_error = ( m_lcount != nbytes );
  } else
  { m_error = ( m_lcount == 0 ); }
  m_reading = false;
  return *this;
}

wxUint32 wxSocketBase::_Read( void* buffer, wxUint32 nbytes ) {
  int total;
  total = GetPushback( buffer, nbytes, false );
  nbytes -= total;
  buffer  = ( char * )buffer + total;
  if( !m_socket ||
      !nbytes ) {
    return total;
  }
  int ret;
  if( m_flags & wxSOCKET_NOWAIT ) {
    m_socket->SetNonBlocking( 1 );
    ret = m_socket->Read( ( char * )buffer, nbytes );
    m_socket->SetNonBlocking( 0 );
    if( ret > 0 ) {
      total += ret;
    }
  } else {
    bool more = true;
    while( more ) {
      if( !( m_flags & wxSOCKET_BLOCK ) && !WaitForRead() ) {
        break;
      }
      ret = m_socket->Read( ( char * )buffer, nbytes );
      if( ret > 0 ) {
        total  += ret;
        nbytes -= ret;
        buffer  = ( char * )buffer + ret;
      }
      more = ( ret > 0 && nbytes > 0 && ( m_flags & wxSOCKET_WAITALL ) );
    }
  }
  return total;
}

wxSocketBase& wxSocketBase::ReadMsg( void* buffer, wxUint32 nbytes ) {
  wxUint32 len, len2, sig, total;
  bool error;
  int old_flags;
  struct {
    unsigned char sig[4];
    unsigned char len[4];
  } msg;
  m_reading = true;
  total = 0;
  error = true;
  old_flags = m_flags;
  SetFlags( ( m_flags & wxSOCKET_BLOCK ) | wxSOCKET_WAITALL );
  if( _Read( &msg, sizeof( msg ) ) != sizeof( msg ) ) {
    goto exit;
  }
  sig = ( wxUint32 )msg.sig[0];
  sig |= ( wxUint32 )( msg.sig[1] << 8 );
  sig |= ( wxUint32 )( msg.sig[2] << 16 );
  sig |= ( wxUint32 )( msg.sig[3] << 24 );
  if( sig != 0xfeeddead ) {
    wxLogWarning( _( "wxSocket: invalid signature in ReadMsg." ) );
    goto exit;
  }
  len = ( wxUint32 )msg.len[0];
  len |= ( wxUint32 )( msg.len[1] << 8 );
  len |= ( wxUint32 )( msg.len[2] << 16 );
  len |= ( wxUint32 )( msg.len[3] << 24 );
  if( len > nbytes ) {
    len2 = len - nbytes;
    len = nbytes;
  } else
  { len2 = 0; }
  if( len ) {
    total = _Read( buffer, len );
    if( total != len ) {
      goto exit;
    }
  }
  if( len2 ) {
    char *discard_buffer = new char[MAX_DISCARD_SIZE];
    long discard_len;
    do {
      discard_len = ( ( len2 > MAX_DISCARD_SIZE ) ? MAX_DISCARD_SIZE : len2 );
      discard_len = _Read( discard_buffer, ( wxUint32 )discard_len );
      len2 -= ( wxUint32 )discard_len;
    } while( ( discard_len > 0 ) && len2 );
    delete [] discard_buffer;
    if( len2 != 0 ) {
      goto exit;
    }
  }
  if( _Read( &msg, sizeof( msg ) ) != sizeof( msg ) ) {
    goto exit;
  }
  sig = ( wxUint32 )msg.sig[0];
  sig |= ( wxUint32 )( msg.sig[1] << 8 );
  sig |= ( wxUint32 )( msg.sig[2] << 16 );
  sig |= ( wxUint32 )( msg.sig[3] << 24 );
  if( sig != 0xdeadfeed ) {
    wxLogWarning( _( "wxSocket: invalid signature in ReadMsg." ) );
    goto exit;
  }
  error = false;
exit:
  m_error = error;
  m_lcount = total;
  m_reading = false;
  SetFlags( old_flags );
  return *this;
}

wxSocketBase& wxSocketBase::Peek( void* buffer, wxUint32 nbytes ) {
  m_reading = true;
  m_lcount = _Read( buffer, nbytes );
  Pushback( buffer, m_lcount );
  if( m_flags & wxSOCKET_WAITALL ) {
    m_error = ( m_lcount != nbytes );
  } else
  { m_error = ( m_lcount == 0 ); }
  m_reading = false;
  return *this;
}

wxSocketBase& wxSocketBase::Write( const void *buffer, wxUint32 nbytes ) {
  m_writing = true;
  m_lcount = _Write( buffer, nbytes );
  if( m_flags & wxSOCKET_WAITALL ) {
    m_error = ( m_lcount != nbytes );
  } else
  { m_error = ( m_lcount == 0 ); }
  m_writing = false;
  return *this;
}

wxUint32 wxSocketBase::_Write( const void *buffer, wxUint32 nbytes ) {
  wxUint32 total = 0;
  if( !m_socket || !buffer || !nbytes ) {
    return 0;
  }
  int ret;
  if( m_flags & wxSOCKET_NOWAIT ) {
    m_socket->SetNonBlocking( 1 );
    ret = m_socket->Write( ( const char * )buffer, nbytes );
    m_socket->SetNonBlocking( 0 );
    if( ret > 0 ) {
      total = ret;
    }
  } else {
    bool more = true;
    while( more ) {
      if( !( m_flags & wxSOCKET_BLOCK ) && !WaitForWrite() ) {
        break;
      }
      ret = m_socket->Write( ( const char * )buffer, nbytes );
      if( ret > 0 ) {
        total  += ret;
        nbytes -= ret;
        buffer  = ( const char * )buffer + ret;
      }
      more = ( ret > 0 && nbytes > 0 && ( m_flags & wxSOCKET_WAITALL ) );
    }
  }
  return total;
}

wxSocketBase& wxSocketBase::WriteMsg( const void *buffer, wxUint32 nbytes ) {
  wxUint32 total;
  bool error;
  struct {
    unsigned char sig[4];
    unsigned char len[4];
  } msg;
  m_writing = true;
  error = true;
  total = 0;
  SetFlags( ( m_flags & wxSOCKET_BLOCK ) | wxSOCKET_WAITALL );
  msg.sig[0] = ( unsigned char ) 0xad;
  msg.sig[1] = ( unsigned char ) 0xde;
  msg.sig[2] = ( unsigned char ) 0xed;
  msg.sig[3] = ( unsigned char ) 0xfe;
  msg.len[0] = ( unsigned char )( nbytes & 0xff );
  msg.len[1] = ( unsigned char )( ( nbytes >> 8 ) & 0xff );
  msg.len[2] = ( unsigned char )( ( nbytes >> 16 ) & 0xff );
  msg.len[3] = ( unsigned char )( ( nbytes >> 24 ) & 0xff );
  if( _Write( &msg, sizeof( msg ) ) < sizeof( msg ) ) {
    goto exit;
  }
  total = _Write( buffer, nbytes );
  if( total < nbytes ) {
    goto exit;
  }
  msg.sig[0] = ( unsigned char ) 0xed;
  msg.sig[1] = ( unsigned char ) 0xfe;
  msg.sig[2] = ( unsigned char ) 0xad;
  msg.sig[3] = ( unsigned char ) 0xde;
  msg.len[0] = msg.len[1] = msg.len[2] = msg.len[3] = ( char ) 0;
  if( ( _Write( &msg, sizeof( msg ) ) ) < sizeof( msg ) ) {
    goto exit;
  }
  error = false;
exit:
  m_error = error;
  m_lcount = total;
  m_writing = false;
  return *this;
}

wxSocketBase& wxSocketBase::Unread( const void *buffer, wxUint32 nbytes ) {
  if( nbytes != 0 ) {
    Pushback( buffer, nbytes );
  }
  m_error = false;
  m_lcount = nbytes;
  return *this;
}

wxSocketBase& wxSocketBase::Discard() {
  char *buffer = new char[MAX_DISCARD_SIZE];
  wxUint32 ret;
  wxUint32 total = 0;
  m_reading = true;
  SetFlags( wxSOCKET_NOWAIT );
  do {
    ret = _Read( buffer, MAX_DISCARD_SIZE );
    total += ret;
  } while( ret == MAX_DISCARD_SIZE );
  delete[] buffer;
  m_lcount = total;
  m_error  = false;
  m_reading = false;
  return *this;
}

bool wxSocketBase::_Wait( long seconds, long milliseconds, wxSocketEventFlags flags ) {
  GSocketEventFlags result;
  long timeout;
  m_interrupt = false;
  if( !m_socket ) {
    return false;
  }
  if( seconds != -1 ) {
    timeout = seconds * 1000 + milliseconds;
  } else
  { timeout = m_timeout * 1000; }
  bool has_event_loop = wxTheApp->GetTraits() ? ( wxTheApp->GetTraits()->GetSocketGUIFunctionsTable() ? true : false ) : false;
  wxDateTime current_time = wxDateTime::UNow();
  unsigned int time_limit = ( current_time.GetTicks() * 1000 ) + current_time.GetMillisecond() + timeout;
  bool done = false;
  bool valid_result = false;
  if( !has_event_loop ) {
    if( timeout > 50 ) {
      m_socket->SetTimeout( 50 );
    } else
    { m_socket->SetTimeout( timeout ); }
  }
  while( !done ) {
    result = m_socket->Select( flags | GSOCK_LOST_FLAG );
    if( result & GSOCK_CONNECTION_FLAG ) {
      m_connected = true;
      m_establishing = false;
      valid_result = true;
      break;
    }
    if( ( result & GSOCK_INPUT_FLAG ) || ( result & GSOCK_OUTPUT_FLAG ) ) {
      valid_result = true;
      break;
    }
    if( result & GSOCK_LOST_FLAG ) {
      m_connected = false;
      m_establishing = false;
      valid_result = ( ( flags & GSOCK_LOST_FLAG ) != 0 );
      break;
    }
    current_time = wxDateTime::UNow();
    int time_left = time_limit - ( ( current_time.GetTicks() * 1000 ) + current_time.GetMillisecond() );
    if( ( !timeout ) || ( time_left <= 0 ) || ( m_interrupt ) ) {
      done = true;
    } else {
      if( has_event_loop ) {
        PROCESS_EVENTS();
      } else {
        if( time_left < 50 ) {
          m_socket->SetTimeout( time_left );
        }
      }
    }
  }
  if( !has_event_loop ) {
    m_socket->SetTimeout( m_timeout * 1000 );
  }
  return valid_result;
}

bool wxSocketBase::Wait( long seconds, long milliseconds ) {
  return _Wait( seconds, milliseconds, GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG | GSOCK_CONNECTION_FLAG | GSOCK_LOST_FLAG );
}

bool wxSocketBase::WaitForRead( long seconds, long milliseconds ) {
  if( m_unread ) {
    return true;
  }
  return _Wait( seconds, milliseconds, GSOCK_INPUT_FLAG | GSOCK_LOST_FLAG );
}

bool wxSocketBase::WaitForWrite( long seconds, long milliseconds ) {
  return _Wait( seconds, milliseconds, GSOCK_OUTPUT_FLAG );
}

bool wxSocketBase::WaitForLost( long seconds, long milliseconds ) {
  return _Wait( seconds, milliseconds, GSOCK_LOST_FLAG );
}

bool wxSocketBase::GetPeer( wxSockAddress& addr_man ) const {
  GAddress *peer;
  if( !m_socket ) {
    return false;
  }
  peer = m_socket->GetPeer();
  if( !peer ) {
    return false;
  }
  addr_man.SetAddress( peer );
  GAddress_destroy( peer );
  return true;
}

bool wxSocketBase::GetLocal( wxSockAddress& addr_man ) const {
  GAddress *local;
  if( !m_socket ) {
    return false;
  }
  local = m_socket->GetLocal();
  addr_man.SetAddress( local );
  GAddress_destroy( local );
  return true;
}

void wxSocketBase::SaveState() {
  wxSocketState *state;
  state = new wxSocketState();
  state->m_flags = m_flags;
  state->m_notify = m_notify;
  state->m_eventmask = m_eventmask;
  state->m_clientData = m_clientData;
  m_states.Append( state );
}

void wxSocketBase::RestoreState() {
  wxList::compatibility_iterator node;
  wxSocketState *state;
  node = m_states.GetLast();
  if( !node ) {
    return;
  }
  state = ( wxSocketState * )node->GetData();
  m_flags = state->m_flags;
  m_notify = state->m_notify;
  m_eventmask = state->m_eventmask;
  m_clientData = state->m_clientData;
  m_states.Erase( node );
  delete state;
}

void wxSocketBase::SetTimeout( long seconds ) {
  m_timeout = seconds;
  if( m_socket ) {
    m_socket->SetTimeout( m_timeout * 1000 );
  }
}

void wxSocketBase::SetFlags( wxSocketFlags flags ) {
  m_flags = flags;
}

extern "C"
void LINKAGEMODE wx_socket_callback( GSocket * ( socket ), GSocketEvent notification, char *cdata ) {
  wxSocketBase *sckobj = ( wxSocketBase * )cdata;
  sckobj->OnRequest( ( wxSocketNotify ) notification );
}

void wxSocketBase::OnRequest( wxSocketNotify notification ) {
  switch( notification ) {
    case wxSOCKET_CONNECTION:
      m_establishing = false;
      m_connected = true;
      break;
    case wxSOCKET_INPUT:
      if( m_reading || !m_socket->Select( GSOCK_INPUT_FLAG ) )
      { return; }
      break;
    case wxSOCKET_OUTPUT:
      if( m_writing || !m_socket->Select( GSOCK_OUTPUT_FLAG ) )
      { return; }
      break;
    case wxSOCKET_LOST:
      m_connected = false;
      m_establishing = false;
      break;
    default:
      break;
  }
  wxSocketEventFlags flag = 0;
  wxUnusedVar( flag );
  switch( notification ) {
    case GSOCK_INPUT:
      flag = GSOCK_INPUT_FLAG;
      break;
    case GSOCK_OUTPUT:
      flag = GSOCK_OUTPUT_FLAG;
      break;
    case GSOCK_CONNECTION:
      flag = GSOCK_CONNECTION_FLAG;
      break;
    case GSOCK_LOST:
      flag = GSOCK_LOST_FLAG;
      break;
    default:
      wxLogWarning( _( "wxSocket: unknown event!." ) );
      return;
  }
  if( ( ( m_eventmask & flag ) == flag ) && m_notify ) {
    if( m_handler ) {
      wxSocketEvent event( m_id );
      event.m_event = notification;
      event.m_clientData = m_clientData;
      event.SetEventObject( this );
      m_handler->AddPendingEvent( event );
    }
  }
}

void wxSocketBase::Notify( bool notify ) {
  m_notify = notify;
}

void wxSocketBase::SetNotify( wxSocketEventFlags flags ) {
  m_eventmask = flags;
}

void wxSocketBase::SetEventHandler( wxEvtHandler& handler, int id ) {
  m_handler = &handler;
  m_id      = id;
}

void wxSocketBase::Pushback( const void *buffer, wxUint32 size ) {
  if( !size ) {
    return;
  }
  if( m_unread == NULL ) {
    m_unread = malloc( size );
  } else {
    void *tmp;
    tmp = malloc( m_unrd_size + size );
    memcpy( ( char * )tmp + size, m_unread, m_unrd_size );
    free( m_unread );
    m_unread = tmp;
  }
  m_unrd_size += size;
  memcpy( m_unread, buffer, size );
}

wxUint32 wxSocketBase::GetPushback( void *buffer, wxUint32 size, bool peek ) {
  if( !m_unrd_size ) {
    return 0;
  }
  if( size > ( m_unrd_size - m_unrd_cur ) ) {
    size = m_unrd_size - m_unrd_cur;
  }
  memcpy( buffer, ( char * )m_unread + m_unrd_cur, size );
  if( !peek ) {
    m_unrd_cur += size;
    if( m_unrd_size == m_unrd_cur ) {
      free( m_unread );
      m_unread = NULL;
      m_unrd_size = 0;
      m_unrd_cur  = 0;
    }
  }
  return size;
}

wxSocketServer::wxSocketServer( const wxSockAddress& addr_man, wxSocketFlags flags )
  : wxSocketBase( flags, wxSOCKET_SERVER ) {
  wxLogTrace( wxTRACE_Socket, _T( "Opening wxSocketServer" ) );
  m_socket = GSocket_new();
  if( !m_socket ) {
    wxLogTrace( wxTRACE_Socket, _T( "*** GSocket_new failed" ) );
    return;
  }
  m_socket->SetLocal( addr_man.GetAddress() );
  if( GetFlags() & wxSOCKET_REUSEADDR ) {
    m_socket->SetReusable();
  }
  if( m_socket->SetServer() != GSOCK_NOERROR ) {
    delete m_socket;
    m_socket = NULL;
    wxLogTrace( wxTRACE_Socket, _T( "*** GSocket_SetServer failed" ) );
    return;
  }
  m_socket->SetTimeout( m_timeout * 1000 );
  m_socket->SetCallback( GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG |
                         GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG,
                         wx_socket_callback, ( char * )this );
}

bool wxSocketServer::AcceptWith( wxSocketBase& sock, bool wait ) {
  GSocket *child_socket;
  if( !m_socket ) {
    return false;
  }
  if( !wait ) {
    m_socket->SetNonBlocking( 1 );
  }
  child_socket = m_socket->WaitConnection();
  if( !wait ) {
    m_socket->SetNonBlocking( 0 );
  }
  if( !child_socket ) {
    return false;
  }
  sock.m_type = wxSOCKET_BASE;
  sock.m_socket = child_socket;
  sock.m_connected = true;
  sock.m_socket->SetTimeout( sock.m_timeout * 1000 );
  sock.m_socket->SetCallback( GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG | GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG,
                              wx_socket_callback, ( char * )&sock );
  return true;
}

wxSocketBase *wxSocketServer::Accept( bool wait ) {
  wxSocketBase* sock = new wxSocketBase();
  sock->SetFlags( m_flags );
  if( !AcceptWith( *sock, wait ) ) {
    sock->Destroy();
    sock = NULL;
  }
  return sock;
}

bool wxSocketServer::WaitForAccept( long seconds, long milliseconds ) {
  return _Wait( seconds, milliseconds, GSOCK_CONNECTION_FLAG );
}

bool wxSocketBase::GetOption( int level, int optname, void *optval, int *optlen ) {
  wxASSERT_MSG( m_socket, _T( "Socket not initialised" ) );
  if( m_socket->GetSockOpt( level, optname, optval, optlen ) != GSOCK_NOERROR ) {
    return false;
  }
  return true;
}

bool wxSocketBase::SetOption( int level, int optname, const void *optval, int optlen ) {
  wxASSERT_MSG( m_socket, _T( "Socket not initialised" ) );
  if( m_socket->SetSockOpt( level, optname, optval, optlen )
      != GSOCK_NOERROR ) {
    return false;
  }
  return true;
}

bool wxSocketBase::SetLocal( wxIPV4address& local ) {
  GAddress* la = local.GetAddress();
  if( la && la->m_addr ) {
    m_localAddress = local;
    return true;
  }
  return false;
}

wxSocketClient::wxSocketClient( wxSocketFlags flags )
  : wxSocketBase( flags, wxSOCKET_CLIENT ) {
}

wxSocketClient::~wxSocketClient() {
}

bool wxSocketClient::DoConnect( wxSockAddress& addr_man, wxSockAddress* local, bool wait ) {
  GSocketError err;
  if( m_socket ) {
    Close();
    delete m_socket;
  }
  m_socket = GSocket_new();
  m_connected = false;
  m_establishing = false;
  if( !m_socket ) {
    return false;
  }
  m_socket->SetTimeout( m_timeout * 1000 );
  m_socket->SetCallback( GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG |
                         GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG,
                         wx_socket_callback, ( char * )this );
  if( !wait ) {
    m_socket->SetNonBlocking( 1 );
  }
  if( GetFlags() & wxSOCKET_REUSEADDR ) {
    m_socket->SetReusable();
  }
  if( !local && m_localAddress.GetAddress() ) {
    local = &m_localAddress;
  }
  if( local ) {
    GAddress* la = local->GetAddress();
    if( la && la->m_addr ) {
      m_socket->SetLocal( la );
    }
  }
  m_socket->SetPeer( addr_man.GetAddress() );
  err = m_socket->Connect( GSOCK_STREAMED );
  if( !wait ) {
    m_socket->SetNonBlocking( 0 );
  }
  if( err != GSOCK_NOERROR ) {
    if( err == GSOCK_WOULDBLOCK ) {
      m_establishing = true;
    }
    return false;
  }
  m_connected = true;
  return true;
}

bool wxSocketClient::Connect( wxSockAddress& addr_man, bool wait ) {
  return ( DoConnect( addr_man, NULL, wait ) );
}

bool wxSocketClient::Connect( wxSockAddress& addr_man, wxSockAddress& local, bool wait ) {
  return ( DoConnect( addr_man, &local, wait ) );
}

bool wxSocketClient::WaitOnConnect( long seconds, long milliseconds ) {
  if( m_connected ) {
    return true;
  }
  if( !m_establishing || !m_socket ) {
    return false;
  }
  return _Wait( seconds, milliseconds, GSOCK_CONNECTION_FLAG |
                GSOCK_LOST_FLAG );
}

wxDatagramSocket::wxDatagramSocket( const wxSockAddress& addr, wxSocketFlags flags )
  : wxSocketBase( flags, wxSOCKET_DATAGRAM ) {
  m_socket = GSocket_new();
  if( !m_socket ) {
    wxFAIL_MSG( _T( "datagram socket not new'd" ) );
    return;
  }
  m_socket->SetLocal( addr.GetAddress() );
  if( flags & wxSOCKET_REUSEADDR ) {
    m_socket->SetReusable();
  }
  if( m_socket->SetNonOriented() != GSOCK_NOERROR ) {
    delete m_socket;
    m_socket = NULL;
    return;
  }
  m_connected = false;
  m_establishing = false;
  m_socket->SetTimeout( m_timeout );
  m_socket->SetCallback( GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG |
                         GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG,
                         wx_socket_callback, ( char* )this );
}

wxDatagramSocket& wxDatagramSocket::RecvFrom( wxSockAddress& addr, void* buf, wxUint32 nBytes ) {
  Read( buf, nBytes );
  GetPeer( addr );
  return ( *this );
}

wxDatagramSocket& wxDatagramSocket::SendTo( const wxSockAddress& addr,
    const void* buf,
    wxUint32 nBytes ) {
  wxASSERT_MSG( m_socket, _T( "Socket not initialised" ) );
  m_socket->SetPeer( addr.GetAddress() );
  Write( buf, nBytes );
  return ( *this );
}

class wxSocketModule : public wxModule {
  public:
    virtual bool OnInit() {
      return true;
    }
    virtual void OnExit() {
      if( wxSocketBase::IsInitialized() ) {
        wxSocketBase::Shutdown();
      }
    }
  private:
    DECLARE_DYNAMIC_CLASS( wxSocketModule )
};

IMPLEMENT_DYNAMIC_CLASS( wxSocketModule, wxModule )

#endif
